package ru.ifmo.thesis.gp;

import java.util.ArrayList;
import java.util.List;

import org.jgap.BaseGeneticOperator;
import org.jgap.Chromosome;
import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.InvalidConfigurationException;
import org.jgap.Population;
import org.jgap.RandomGenerator;

import ru.ifmo.thesis.domain.Job;

public class MyMutationOperator extends BaseGeneticOperator {

	private static final long serialVersionUID = 1L;

	private double mutationPercent;
	
	private int movings;
	
	public MyMutationOperator(Configuration aConfiguration, double mutationPercent, int movings)
			throws InvalidConfigurationException {
		super(aConfiguration);
		this.mutationPercent = mutationPercent;
		this.movings = movings;
	}


	@Override
	public void operate(Population aPopulation, List chromosomes) {
		RandomGenerator rg = getConfiguration().getRandomGenerator();
		List<Chromosome> chromoCopy = new ArrayList<Chromosome>(
				(List<Chromosome>) aPopulation.getChromosomes());
		
		for (Chromosome c : chromoCopy) {
			if (rg.nextDouble() < mutationPercent) {
				chromosomes.add(mutate(c));
			}
		}
	}

	private Chromosome mutate(Chromosome c) {
		Chromosome mutant = c;
		for (int i = 0; i < movings; i++) {
			mutant = applyMutation(c);
		}
		return mutant;
	}


	private Chromosome applyMutation(Chromosome c) {
		Gene[] gene = c.getGenes();
		
		//select 2 workers
		
		RandomGenerator rg = getConfiguration().getRandomGenerator();
		
		WorkPlanGene first = (WorkPlanGene) gene[rg.nextInt(gene.length)];
		WorkPlanGene second = (WorkPlanGene) gene[rg.nextInt(gene.length)];
		
		ArrayList<Job> firstAllele = (ArrayList) first.getAllele();
		ArrayList<Job> secondAllele = (ArrayList) second.getAllele();
		
		//choose between swaping and moving
		if (rg.nextBoolean() && !(firstAllele.isEmpty() || secondAllele.isEmpty())) {
			swapping(rg, firstAllele, secondAllele);
		} else {
			moving(rg, firstAllele, secondAllele);
		}
		
		first.setAllele(firstAllele);
		second.setAllele(secondAllele);
		
		return c;
	}


	private void moving(RandomGenerator rg, ArrayList<Job> firstAllele,
			ArrayList<Job> secondAllele) {
		if (firstAllele.isEmpty()) {
			if (!secondAllele.isEmpty()) {
				int movingIndex = rg.nextInt(secondAllele.size());
				firstAllele.add(secondAllele.remove(movingIndex));
			}
		} else {
			int movingIndex = rg.nextInt(firstAllele.size());
			Job value = firstAllele.remove(movingIndex);
			
			if (secondAllele.isEmpty()) {
				secondAllele.add(value);
			} else {
				int insertingIndex = rg.nextInt(secondAllele.size());
				secondAllele.add(insertingIndex, value);
			}
		}
	}


	private void swapping(RandomGenerator rg, ArrayList<Job> firstAllele,
			ArrayList<Job> secondAllele) {
		int swapIndex1 = rg.nextInt(firstAllele.size());
		int swapIndex2 = rg.nextInt(secondAllele.size());
		
		Job tmp = firstAllele.get(swapIndex1);
		firstAllele.set(swapIndex1, secondAllele.get(swapIndex2));
		secondAllele.set(swapIndex2, tmp);
	}


	@Override
	public int compareTo(Object o) {
		return 0;
	}
}
